#include <Driver.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

//#include <iostream>

namespace{
    class Socket{
    public:
        Socket() : sock_(socket(PF_INET, SOCK_STREAM, 0)){
            if (sock_ < 0)
                throw std::runtime_error("can't create socket");
        }
        ~Socket(){
            shutdown(sock_, SHUT_RDWR);
            close(sock_);
        }
        operator int() const {return sock_;}
    private:
        int sock_;
    };
    bool is_alive(const CExpert::DBChecker::conn_params& cp){
        try{
            Socket sock;

            struct sockaddr_in server = {0};
            server.sin_family = AF_INET;
            //host
            struct hostent *he;
            if ((he = gethostbyname(cp.host.c_str())) == NULL){
                //do not set host - will fail in connect()
            } else{
                memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
            }
            //port
            server.sin_port = htons(atoi(cp.port.c_str()));
            //set to non-blocking mode
            int flags = fcntl(sock, F_GETFL);
            if (flags == -1)
                flags = 0;
            if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)
                throw std::runtime_error("can't set socket to O_NONBLOCK");
            //now connect
            int status = connect(sock, (sockaddr*)&server, sizeof(server));
            if (status == -1){
                if (errno == EINPROGRESS){
                    //wait for 1 second to connect
                    fd_set fds;
                    FD_ZERO(&fds);
                    FD_SET(sock, &fds);
                    timeval timeout;
                    timeout.tv_sec = 1; timeout.tv_usec = 0;
                    //we have exactly one FD in fds;
                    if (select(sock + 1, NULL, &fds, NULL, &timeout) == 1){
                        socklen_t status_len = sizeof(status);
                        getsockopt(sock, SOL_SOCKET, SO_ERROR, &status, &status_len);
                    }
                }
            }
            return status == 0;
        } catch (const std::exception& ex){
            return false;
        }
        //std::cout << cp.host << ":" << cp.port << " == " << (status == 0) << ", " << status << " : " << EINPROGRESS << ", " << errno << std::endl;
    }
}
void tcp_init(){
    static int tcp_init_v = CExpert::register_driver("tcp", &is_alive);
    (void)tcp_init_v;
}
